/*
 * https://yk2012.github.io/sgg_webpack5/
 */

const path = require("path");
/*
 * 自动引入 main.js
 * CodeSplit 代码分割 打包生成的文件进行分割，生成多个 js 文件按需加载
 */
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 分离 css ，通过link标签引入避免白屏
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 优化和压缩 CSS
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
// 压缩js插件 内置
const TerserWebpackPlugin = require("terser-webpack-plugin");
// 获取进程数来开启多进程处理压缩 js 或者 ESLint
const os = require("os");
const threads = os.cpus().length; // cpu 核数
// 压缩图片
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
// preload: 立即加载资源  prefetch:  空闲加载资源
const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin");
// 自定义插件实验
const TestPlugin = require("./plugins/test-plugin");
// 打包输出文件添加注释
const BannerWebpackPlugin = require("./plugins/banner-webpack-plugin");
// 分析 webpack 打包资源大小
const AnalyzeWebpackPlugin = require("./plugins/analyze-webpack-plugin");
// 内联 runtime
const InlineChunkWebpackPlugin = require("./plugins/InlineChunkWebpackPlugin");

// 封装样式 loader
function getStyleLoader(otherStyleLoader) {
  return [
    MiniCssExtractPlugin.loader,
    "css-loader",
    // 位置不能变
    {
      loader: "postcss-loader",
      options: {
        postcssOptions: {
          plugins: ["postcss-preset-env"],
        },
      },
    },
    otherStyleLoader,
  ].filter(Boolean);
}

module.exports = {
  mode: "production",
  devtool: "source-map", // 包含行列映射
  // 单入口
  // entry: "./src/main.js",
  // 多入口
  entry: {
    main: "./src/main.js",
    app: "./src/app.js",
  },
  output: {
    path: undefined,
    // 多入口配置文件名
    // Network Cache：使用 .[contenthash:10] 和 runtime 文件来处理静态资源缓存
    filename: "js/[name].[contenthash:10].js", // 入口文件打包输出资源命名方式
    chunkFilename: "js/[name].[contenthash:10].chunk.js", // 动态导入输出资源命名方式
    assetModuleFilename: "media/[name].[hash][ext]", // 图片、字体等资源命名方式（注意用hash）
    clean: true, // 自动将上次打包目录资源清空
  },
  // 关闭性能分析警告 提高打包速度
  performance: false,
  // 加载器 loader
  module: {
    rules: [
      {
        // 当规则匹配时，只使用第一个匹配规则
        oneOf: [
          {
            test: /\.css$/i,
            use: getStyleLoader(),
          },
          {
            test: /\.less$/i,
            use: getStyleLoader("less-loader"),
          },
          {
            test: /\.s[ac]ss$/i,
            use: getStyleLoader("sass-loader"),
          },
          {
            test: /\.styl$/,
            use: getStyleLoader("stylus-loader"),
          },
          // 处理图片
          {
            test: /\.(png|jpe?g|gif|webp|svg)$/,
            type: "asset", // 转 base64 后输出
            parser: {
              dataUrlCondition: {
                maxSize: 40 * 1024,
              },
            },
          },
          // 处理字体图标或者其他不需要处理的文件
          {
            test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
            type: "asset/resource",
            generator: {
              filename: "assets/[hash][ext][query]",
            },
          },
          // 使用 Babel 编译 ES6或其他高级 语法为普通js
          {
            test: /\.m?js$/,
            exclude: /(node_modules|bower_components)/, // 排除 node_modules 下文件，其他文件不处理
            // include: path.resolve(__dirname,'./src/'), // 只处理 src 下文件
            use: [
              {
                loader: "thread-loader", // 开启多进程
                options: {
                  works: threads, // 进程数量
                },
              },
              ,
              {
                loader: "babel-loader",
                options: {
                  presets: ["@babel/preset-env"],
                  // 开启 Babel 编译缓存，提高第二次及以后打包速度
                  cacheDirectory: true,
                  //关闭缓存文件压缩
                  cacheCompression: false,
                  // 减少代码体积 禁用了 Babel 自动对每个文件的 runtime 注入，而是引入 @babel/plugin-transform-runtime 并且使所有辅助代码从这里引用
                  plugins: ["@babel/plugin-transform-runtime"],
                },
              },
            ],
          },
        ],
      },
      // 清理 console.log() loader
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: "./loaders/clean-log-loader/clean-log-loader.js",
      },
    ],
  },
  plugins: [
    // 自动引入 main.js
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "public/index.html"),
      filename: "index.html",
    }),
    // 分离 css
    new MiniCssExtractPlugin({
      // 定义输出文件名和目录
      filename: "css/[name].[contenthash:10].css",
      chunkFilename: "css/[name].[contenthash:10].chunk.css",
    }),
    // 压缩css
    // new CssMinimizerPlugin(),
    new PreloadWebpackPlugin({
      rel: "preload", // preload兼容性更好
      as: "script",
      // rel: "prefetch", // prefetch兼容性更差
    }),
  ],
  optimization: {
    minimizer: [
      new BannerWebpackPlugin({
        author: "kong",
      }),
      // 压缩css
      new CssMinimizerPlugin(),
      // 压缩js
      new TerserWebpackPlugin({
        parallel: threads, // 开启多进程和设置进程数量
      }),
      // 无损压缩图片
      new ImageMinimizerPlugin({
        minimizer: {
          implementation: ImageMinimizerPlugin.imageminGenerate,
          options: {
            plugins: [
              ["gifsicle", { interlaced: true }],
              ["jpegtran", { progressive: true }],
              ["optipng", { optimizationLevel: 5 }],
              [
                "svgo",
                {
                  plugins: [
                    "preset-default",
                    "prefixIds",
                    {
                      name: "sortAttrs",
                      params: {
                        xmlnsOrder: "alphabetical",
                      },
                    },
                  ],
                },
              ],
            ],
          },
        },
      }),
      new TestPlugin(),
      new AnalyzeWebpackPlugin(),
      new InlineChunkWebpackPlugin([/runtime(.*)\.js/]),
    ],
    // 代码分割配置
    splitChunks: {
      chunks: "all", // 对所有模块都进行分割
      // 以下是默认值
      // minSize: 20000, // 分割代码最小的大小
      // minRemainingSize: 0, // 类似于minSize，最后确保提取的文件大小不能为0
      // minChunks: 1, // 至少被引用的次数，满足条件才会代码分割
      // maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量
      // maxInitialRequests: 30, // 入口js文件最大并行请求数量
      // enforceSizeThreshold: 50000, // 超过50kb一定会单独打包（此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests）
      // cacheGroups: { // 组，哪些模块要打包到一个组
      //   defaultVendors: { // 组名
      //     test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块
      //     priority: -10, // 权重（越大越高）
      //     reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块，则它将被重用，而不是生成新的模块
      //   },
      //   default: { // 其他没有写的配置会使用上面的默认值
      //     minChunks: 2, // 这里的minChunks权重更大
      //     priority: -20,
      //     reuseExistingChunk: true,
      //   },
      // },
      // 修改配置
      cacheGroups: {
        // 组，哪些模块要打包到一个组
        // defaultVendors: { // 组名
        //   test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块
        //   priority: -10, // 权重（越大越高）
        //   reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块，则它将被重用，而不是生成新的模块
        // },
        default: {
          // 其他没有写的配置会使用上面的默认值
          minSize: 0, // 我们定义的文件体积太小了，所以要改打包的最小文件体积
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
    // 提取runtime文件 处理静态资源 hash 改变缓存失效问题
    runtimeChunk: {
      name: (entrypoint) => `runtime~${entrypoint.name}`, // runtime文件命名规则
    },
  },
};
